Skip to content

Crash destroying imgui context in dynamic fonts branch #8653

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Aarkham opened this issue May 23, 2025 · 4 comments
Open

Crash destroying imgui context in dynamic fonts branch #8653

Aarkham opened this issue May 23, 2025 · 4 comments

Comments

@Aarkham
Copy link

Aarkham commented May 23, 2025

Version/Branch of Dear ImGui:

Version 1.92.0 WIP, Branch: dynamic_fonts

Back-ends:

Custom

Compiler, OS:

Windows 10 Visual Studio

Full config/build information:

Dear ImGui 1.92.0 WIP (19195)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=201703
define: _WIN32
define: _WIN64
define: _MSC_VER=1944
define: _MSVC_LANG=201703
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK
--------------------------------
io.BackendPlatformName: Entity ComputerPane
io.BackendRendererName: Entity ComputerPane Drawer
io.ConfigFlags: 0x00000000
io.ConfigViewportsNoDecoration
io.ConfigNavCaptureKeyboard
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x00000010
 RendererHasTextures
--------------------------------
io.Fonts: 1 fonts, Flags: 0x00000006, TexSize: 2048,2048
io.Fonts->FontLoaderName: "stb_truetype"
io.DisplaySize: 2560.00,1715.07
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00

Details:

Crash destroying imgui context

In my game I have a debug imgui and also some computers running imgui as UI. So, I have a debug context and may have several ingame contexts, they are not multi threaded. I attach a video showing three ingame imguis and later show the debug UI (in it you can see there are 14 ingame UIs).

The debug UI does not implement the ImGuiBackendFlags_RendererHasTextures but the ingame UIs do.

The debug UI does not have any issue that I'm aware of.

This is the code that creates the ingame UIs:

bool ComputerPane::Create(int aId
                      , const math::Vector2f& aO,const math::Vector2f& aP1,const math::Vector2f& aP2
                      , float32_t aZPos
                      , float32_t aDisplayWidth
                      , float32_t aFontSize,bool aDrawMouse
                      , const std::string& aColourMode
                      , std::unique_ptr<ComputerPaneAction>&& aComputerPaneAction
                      , const ActionParms_t& aParms)
{
  AC_ASSERT_MSG(aDisplayWidth>0.0f,"aDisplayWidth must be greater than 0.");
  AC_ASSERT_MSG(aFontSize>0.0f,    "aFontSize must be greater than 0.");

  AC_ASSERT_MSG(!m_ImGuiTexture,"ComputerPane texture already created.");
  AC_ASSERT_MSG(!m_ImGuiContext,"ComputerPane context already created.");

  m_Id=aId;
  m_DisplayWidth=aDisplayWidth;
  SetPosition(aO,aP1,aP2,math::Vector2f(0.0f,0.0f));

  m_ZPos=aZPos;
  m_Active=true;
  m_Enabled=true;

  m_DrawMouseCursor=aDrawMouse;

//  m_Position.SetCenter(m_Center.m_X,m_Center.m_Y);

  m_ComputerPaneAction=std::move(aComputerPaneAction);
  AC_ASSERT_MSG(m_ComputerPaneAction,"Can not get aComputerPaneAction.");

  m_Parms.Assign(aParms);

  graphics::Device* dev=ac::GetApp().GetGraphicsDevice();
  AC_ASSERT_MSG(dev,"Can not get graphics::Device.");

  ImGuiContext* prev_context=ImGui::GetCurrentContext();
  ImGui::SetCurrentContext(nullptr);
  m_ImGuiContext=ImGui::CreateContext();
  AC_ASSERT_MSG(m_ImGuiContext,"Error creating m_ImGuiContextd.");

  ImGui::SetCurrentContext(m_ImGuiContext);
  ImGuiIO& imgui_io=ImGui::GetIO();
  imgui_io.DisplaySize.x=aDisplayWidth;
  imgui_io.DisplaySize.y=aDisplayWidth*static_cast<float>(m_Position.GetHeight())/static_cast<float>(m_Position.GetWidth());
  imgui_io.IniFilename=nullptr;
  imgui_io.BackendPlatformName="Entity ComputerPane";
  imgui_io.BackendRendererName="Entity ComputerPane Drawer";
#if defined IMGUI_HAS_TEXTURES
  imgui_io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures;
#endif
  imgui_io.MouseDrawCursor=false;



  const graphics::FontManager* font_manager=dev->GetTextRenderer().GetFontManager();


#if defined IMGUI_HAS_TEXTURES

  m_FontManager=font_manager;
  m_FontSize=/*trunc*/(kTestFontSize * aFontSize);

  m_CustomLoader=new ImFontLoader;
  AC_ASSERT_MSG(m_CustomLoader, "new m_CustomLoader failed.");
  m_CustomLoader->Name="ImGuiSDF";
  m_CustomLoader->FontSrcInit=FontSrcInit;
  m_CustomLoader->FontSrcContainsGlyph=FontSrcContainsGlyph;
  m_CustomLoader->FontBakedLoadGlyph=FontBakedLoadGlyph;

  imgui_io.Fonts->Flags|=ImFontAtlasFlags_NoBakedLines;
  imgui_io.Fonts->Flags|=ImFontAtlasFlags_NoMouseCursors;


  ImFontConfig empty_font;
  strcpy(empty_font.Name, "cpane");
  empty_font.FontLoader=m_CustomLoader;
  empty_font.SizePixels=m_FontSize;
  imgui_io.Fonts->TexMinWidth=kTextureWidth;
  imgui_io.Fonts->TexMinHeight=kTextureHeight;
  empty_font.FontData=this;

  //m_ImGuiContext->FontAtlasOwnedByContext=false;

  m_ImFont=imgui_io.Fonts->AddFont(&empty_font);

  imgui_io.Fonts->TexUvWhitePixel=ImVec2(0.0f,0.0f);
  SetBgColour(m_ImFont,IM_COL32(139,108,0,255));
  ReserveWhiteSpace(imgui_io.Fonts,*font_manager);
  Preload(m_ImFont, m_FontSize,(const ImWchar*)"\xe1\xe9\xed\xf3\xfa\xb4"
                                        "abcdefghijklmnopqrstuvwxyz"
                                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
                                        ",;.:#@!?<>[](){}/&%$_=-+*'~|\\"
                                        L"\x0080\x0081\x0082\x0083\x0084\x0085\x0086\x0087\x0088\x0089\x00A6\x00A7\x007f"
                                        L"\x00a1\x00bf"
                                        L"\x00d1\x00f1"
                                        L"\x008a" // Bug
                                        "©®"
                                        "\""
                                        L"¡¿"
                                        L"\x20ac" // Euro
                                        L"\x2026" // Horizontal ellipsis
                                        L"\xfffd" // Unknown char
                                        L"\xf245" // Mouse Cursor
                                        L"\xf118" // Smile
                                        L"\xf14d" // Share Square
                                        L"\xf1f8" // Trash
                                        L"\xf01c" // Inbox
                                        L"\xf0e0" // Envelop
                                        L"\xf55a" // BackSpace
                                        L"\xf1bb" // Tree
                                        L"\xf0f3" // Bell
                                        L"\xf30a" // Long arrow left
                                        L"\xf30b" // Long arrow right
                                        L"\xf7c0" // Satellite dish
                                        L"\xf085" // Cogs
                                        L"\xf04d" // Stop
                                        L"\xf071" // Exclamation triangle
                                        L"\xf059" // Question circle
                                        L"\xf084" // Key
                                        L"\xf5d2" // Atom
                                        L"\xf5df" // Reversi icon
                                        L"\xf044" // Edit
                                        L"\xf439" // Chess
                                        L"\xf43c" // Chess board
                                        L"\xf024" // Flag
                                        L"\xf017" // Clock
                                        L"\xf3ed" // Shield
                                        L"\xf003" // Envelope o
                                        L"\xf2b7" // Envelope o open
                                        L"\xf014" // Trash o
                                        L"\xf11d" // Flag o
                                        L"\xf011" // Power Off
                                        L"\xf28c" // Pause circle o
                                        L"\xf29c" // Question circle o
                                        L"\xf0e2" // Undo
                                        L"\xf01e" // Repeat
                                        L"\xf5df" // Car battery
                           );
  m_ImFont->Flags|=ImFontFlags_LockBakedSizes;
  m_ImFont->Scale=1.0f;
  imgui_io.Fonts->TexUvWhitePixel.x=0.0f;
  imgui_io.Fonts->TexUvWhitePixel.y=0.0f;

#else


  unsigned char* pixels;
  int width, height;

  ImFontConfig font_config;
  //font_config.RasterizerDensity=(22.0f/120.0f);
  font_config.SizePixels=font_manager->GetCurrentFontSize();
  m_ImFont=imgui_io.Fonts->AddFontDefault(&font_config);


  std::tuple<unsigned char*,int,int> ret=CreateImGuiSDF(imgui_io,m_ImFont
                                                       ,font_manager,font_config.SizePixels
                                                       ,
                                                        L"\xe1\xe9\xed\xf3\xfa\xb4"
                                                        "abcdefghijklmnopqrstuvwxyz"
                                                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
                                                        ",;.:#@!?<>[](){}/&%$_=-+*'~|\\"
                                                        "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\xA6\xA7\x7f"
                                                        "\xa1\xbf"
                                                        "ñÑ"
                                                        L"\x008a" // Bug
                                                        "©®"
                                                        "\""
                                                        L"¡¿"
                                                        L"\x20ac" // Euro
                                                        L"\x2026" // Horizontal ellipsis
                                                        L"\xfffd" // Unknown char
                                                        L"\xf245" // Mouse Cursor
                                                        L"\xf118" // Smile
                                                        L"\xf14d" // Share Square
                                                        L"\xf1f8" // Trash
                                                        L"\xf01c" // Inbox
                                                        L"\xf0e0" // Envelop
                                                        L"\xf55a" // BackSpace
                                                        L"\xf1bb" // Tree
                                                        L"\xf0f3" // Bell
                                                        L"\xf30a" // Long arrow left
                                                        L"\xf30b" // Long arrow right
                                                        L"\xf7c0" // Satellite dish
                                                        L"\xf085" // Cogs
                                                        L"\xf04d" // Stop
                                                        L"\xf071" // Exclamation triangle
                                                        L"\xf059" // Question circle
                                                        L"\xf084" // Key
                                                        L"\xf5d2" // Atom
                                                        L"\xf044" // Edit
                                                        L"\xf439" // Chess
                                                        L"\xf43c" // Chess board
                                                        L"\xf024" // Flag
                                                        L"\xf017" // Clock
                                                        L"\xf3ed" // Shield
                                                        L"\xf003" // Envelope o
                                                        L"\xf2b7" // Envelope o open
                                                        L"\xf014" // Trash o
                                                        L"\xf11d" // Flag o
                                                        L"\xf011" // Power Off
                                                        L"\xf28c" // Pause circle o
                                                        L"\xf29c" // Question circle o
                                                        L"\xf0e2" // Undo
                                                        L"\xf01e" // Repeat
                                                       );
  pixels=std::get<0>(ret);
  width=std::get<1>(ret);
  height=std::get<2>(ret);

  static const float kImGuiDefFontSize=13.0f;
  m_ImFont->Scale=kImGuiDefFontSize /font_manager->GetCurrentFontSize(); // font1->FontSize size of default font.
  m_ImFont->Scale*=aFontSize;

  m_ImGuiTexture=CreateTexture(*dev,width,height);
  if(!m_ImGuiTexture)
    {
      LOG_PRINT_F("Can not create font texture.");
      Destroy();
      return false;
    }
  m_ImGuiTexture->SetData(width, height,pixels);
  imgui_io.Fonts->TexID = m_ImGuiTexture->GetTextureId();
  imgui_io.Fonts->Flags|=ImFontAtlasFlags_NoBakedLines;

  m_ImGuiTexture->SetName("ComputerPane SDF Font");

#endif

  ImGui::GetStyle().ScrollbarSize*=1.2f;
  if(aColourMode=="light")
    {
      ImGui::StyleColorsLight();
    }
  else if(aColourMode=="dark")
    {
      ImGui::StyleColorsDark();
    }
  else if(aColourMode=="classic")
    {
      ImGui::StyleColorsClassic();
    }

  imgui_io.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen);

  if(m_ComputerPaneAction->UsesImPlot())
    {
      m_ImPlotContext=ImPlot::CreateContext();
    }

  if(prev_context)
    {
      ImGui::SetCurrentContext(prev_context);
    }

  m_DisplayWidth =imgui_io.DisplaySize.x;
  m_DisplayHeight=imgui_io.DisplaySize.y;
#if defined IMGUI_HAS_TEXTURES
  m_ImGuiTexture=ComputerPane::m_CommonImGuiTexture.get();
#endif
  return true;
}

Some notes:

  • The m_ variables are class members.
  • I do not share the atlas but I do share the texture memory. I use SDF fonts and I know in advance all the chars that I'm going to use so I preload them.
  • FontManager is the class I use that has the SDF data.
  • m_ImFont is a ImFont* that I use in every step to push/pop the font.
  • SetBgColour() sets the texture background color. It would be nice that you allowed to customize this when creating the texture data for the atlas.
  • ReserveWhiteSpace() Sets some texture memory for drawing the solid parts of the UI. In previous versions of dear imgui I used that the TexUvWhitePixel was 0,0 but now it changes with the atlas. Is possible to set if fixed to 0,0?
  • Preload() preloads all the chars I use in the game. It basically calls ImFontBaked::FindGlyph().

This is the code I use to destroy the ingame UIs:

void ComputerPane::Destroy()
{
  AC_ASSERT_MSG(m_ImGuiContext,"ComputerPane context already destroyed.");

  ImGuiContext* prev_context=ImGui::GetCurrentContext();
  ImGui::SetCurrentContext(m_ImGuiContext);

  if(m_ComputerPaneAction)
    {
      m_ComputerPaneAction->Clear();
      m_ComputerPaneAction.reset();
    }

  if(m_ImPlotContext)
    {
      ImPlot::SetCurrentContext(nullptr);
      ImPlot::DestroyContext(m_ImPlotContext);
      m_ImPlotContext=nullptr;
    }



#if defined IMGUI_HAS_TEXTURES
  //IM_DELETE(m_ImFont); // m_ImFont, destroyed by imgui
  m_ImFont=nullptr;

  m_FontSize=0;
  m_FontManager=nullptr;
#else

  // m_ImFont, destroyed by imgui
  m_ImFont=nullptr;
#endif

  ImGui::SetCurrentContext(prev_context);
  ImGui::DestroyContext(m_ImGuiContext);



#if defined IMGUI_HAS_TEXTURES

  // m_CustomLoader, destroyed by imgui
  //delete m_CustomLoader;
  m_CustomLoader=nullptr;
#endif

  m_ImGuiContext=nullptr;

  DestroyTexture();
  m_ImGuiTexture=nullptr;


  m_ImGuiDrawData=nullptr;

  m_MousePos.SetZero();
  m_MouseDown=false;
  m_Active=false;
  m_Enabled=false;
}

The game crashes after calling this function while destroying the m_Parms member. But if I comment out this member then it crashes in another place. Also the game does not crash if I compile it with the docking branch.

Let me know if you need more information.

Carlos

Screenshots/Video:

Image

Minimal, Complete and Verifiable Example code:

@ocornut
Copy link
Owner

ocornut commented May 24, 2025

Sorry this is way too much code, you are not even providing details about the crash. I cannot magically guess what’s wrong without more details.

@ocornut
Copy link
Owner

ocornut commented May 24, 2025

Try to craft a minimal standardalone repro that exhibit the problem.

@GamingMinds-DanielC
Copy link
Contributor

In ComputerPane::Create() you have an early out in case of failure where you forgot to restore the previous context. That's probably not the reason for your crash, but still a bug.

As for the unspecified crash: test in a debug build with assertions enabled. The exact location of the crash (or triggered assertion) is important, maybe even including the call stack and values of relevant variables.

@Aarkham
Copy link
Author

Aarkham commented May 26, 2025

@GamingMinds-DanielC Thanks for the help. That code was in the part where ImGuiBackendFlags_RendererHasTextures is false, so it could not be the reason for the crash, but a bug is a bug and now is fixed :)

I've solved the issue. The problem was here:

  ImFontConfig empty_font;
  strcpy(empty_font.Name, "cpane");
  empty_font.FontLoader=m_CustomLoader;
  empty_font.SizePixels=m_FontSize;
  imgui_io.Fonts->TexMinWidth=kTextureWidth;
  imgui_io.Fonts->TexMinHeight=kTextureHeight;
  empty_font.FontData=this;  //  <----------   BUG

In the function void ImFontAtlasFontDestroySourceData() imgui frees the field FontData so my class was being freed and the rest of the destructor was handling corrupted data.

Now I have added:

  empty_font.FontData=this;
  empty_font.FontDataSize=sizeof(ComputerPane);
  empty_font.FontDataOwnedByAtlas=false;

And the game can close Ok.

I'm not sure if this is the proper fix, I was doing this because I need to access the class from the callbacks of ImFontLoader. I tried to use the FontLoaderData member but there is an assert that makes sure that is null. I also tried to inherit from ImFontLoader but it did not work. What is the recommended way to do this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants